#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <cf_std.h>
#include <cf_common.h>
#include "p7zip_helper.h"
#include "p7zg_log.h"

int P7zipInit(P7zip* this)
{
    int i = 0, n;
    int paraCount;
     P7ZG_DBG("debug==============\n");   
    paraCount = 10;
    if(this->fileCount>0){
        paraCount += this->fileCount;
    }
    
    //memset(this, 0, sizeof(P7zip));
    this->pParams = (const char**)CFMalloc(sizeof(const char*)*(paraCount));
    this->pParams[i++] = this->bin;
    if(!this->action){
        return -1;
    }else{
        this->pParams[i++]= this->action;
    }
    if(this->typePara){
        this->pParams[i++] = this->typePara;    
    }
    if(this->cl >= 0){
        this->clPara = CFStringNew("-mx=1000");
        sprintf(CFStringGetStr(this->clPara), "-mx=%d", this->cl);
        this->pParams[i++] = CFStringGetStr(this->clPara);
    }
    if(this->mt > 0){
        this->mtPara = CFStringNew("-mmt=1000");
        sprintf(CFStringGetStr(this->mtPara), "-mmt=%d", this->mt);
        this->pParams[i++] = CFStringGetStr(this->mtPara);  
    }
    if(this->passwd){
        P7ZG_DBG("this->passwd: %s\n", this->passwd);
        this->passwdPara = CFStringNew("-p");
        CFStringSafeCat(this->passwdPara, this->passwd);
        this->pParams[i++] = CFStringGetStr(this->passwdPara);
    }
    if(this->volSize > 0){
        this->volSizePara = CFStringNew("0123456789 0123456789 0123456789 0123456789");
        sprintf(CFStringGetStr(this->volSizePara), "-v%lld", this->volSize);
        this->pParams[i++] = CFStringGetStr(this->volSizePara);
    }
    
    if(this->si){
        this->pParams[i++] = "-si";
    } 
    
    if(this->so){
        this->pParams[i++] = "-so";
    }
    
    if(this->archive){
        P7ZG_DBG("this->archive:%s\n", this->archive);
        this->archivePara = CFStringNew(this->archive);
        P7ZG_DBG("this->archive:%s\n", CFStringGetStr(this->archivePara));
        P7ZG_DBG("i=%d\n", i);
        this->pParams[i++] = CFStringGetStr(this->archivePara);
    }
    
    if(!this->si && this->files && this->fileCount>0){
        for(n=0; n<this->fileCount; n++){
            this->pParams[i++] = this->files[n];
        }
    }
    
    if(this->outDir){
        this->outDirPara = CFStringNew("-o");
        CFStringSafeCat(this->outDirPara, this->outDir);
        this->pParams[i++] = CFStringGetStr(this->outDirPara);
    }
    
    this->pParams[i++] = NULL;
    P7ZG_DBG("debug==============\n");
    for(n=0; this->pParams[n]; n++){
        P7ZG_DBG("%s\n", this->pParams[n]);
    }
    return 0;
}

int P7zipExit(P7zip* this)
{
    if(this->outDir){
        CFStringFree(this->outDirPara);
    }
    if(this->archive){
        CFStringFree(this->archivePara);
    }
    if(this->volSize > 0){
        CFStringFree(this->volSizePara);
    }
    if(this->passwd)
        CFStringFree(this->passwdPara);
    if(this->mt > 0)
        CFStringFree(this->mtPara);
    if(this->cl > 0)
        CFStringFree(this->clPara);
    CFFree(this->pParams);
    this->pParams = NULL;
    return 0;
}

int P7zipSystem(P7zip* this)
{
    return CFSystem(this->pParams, NULL, NULL);
}

int P7zipPopen(P7zip* this, int* fromStdoutFd, pid_t* pid)
{
    return CFPopen(this->pParams, fromStdoutFd, NULL, NULL, pid);
}

int Compress7z(int fileCount, const char* files[], const char* archive, int cl, int mt, long long vol, const char* passwd)
{
    int ret;
    P7zip p7z;
    
    memset(&p7z, 0, sizeof(P7zip));
    p7z.bin = P7Z_GUI;
    p7z.action = P7Z_ACTION_ADD;
    p7z.typePara = P7Z_TYPE_7Z;
    p7z.files = files;
    p7z.fileCount = fileCount;
    p7z.archive = archive;
    p7z.cl = cl;
    p7z.mt = mt;
    p7z.volSize = vol;
    p7z.passwd = passwd;
    p7z.so = FALSE;
    p7z.si = FALSE;
    if(P7zipInit(&p7z))
        return -1;
    ret = P7zipSystem(&p7z);
    P7zipExit(&p7z);
    return ret;
}

int CompressZip(int fileCount, const char* files[], const char* archive, int cl, int mt, long long vol, const char* passwd)
{
    int ret;
    P7zip p7z;
    memset(&p7z, 0, sizeof(P7zip));
    p7z.bin = P7Z_GUI;
    p7z.action = P7Z_ACTION_ADD;
    p7z.typePara = P7Z_TYPE_ZIP;
    p7z.files = files;
    p7z.fileCount = fileCount;
    p7z.archive = archive;
    p7z.cl = cl;
    p7z.mt = mt;
    p7z.volSize = vol;
    p7z.passwd = passwd;
    p7z.so = FALSE;
    p7z.si = FALSE;
    if(P7zipInit(&p7z))
        return -1;
    ret = P7zipSystem(&p7z);
    P7zipExit(&p7z);
    return ret;
}

int CompressTarGzip(int fileCount, const char* files[], const char* archive, int cl, int mt, long long vol)
{
    P7zip p7zTar, p7zGzip;
    int outFd, stdinFd;
    pid_t pid;
    int ret;
    int status;
    
    memset(&p7zTar, 0, sizeof(P7zip));
    p7zTar.bin = P7Z_CLI;
    p7zTar.action = P7Z_ACTION_ADD;
    p7zTar.typePara = P7Z_TYPE_TAR;
    p7zTar.files = files;
    p7zTar.fileCount = fileCount;
    p7zTar.archive = "lijuan_dummy.tar";
    p7zTar.cl = -1;
    p7zTar.mt = mt;
    p7zTar.volSize = -1;
    p7zTar.passwd = NULL;
    p7zTar.so = TRUE;
    p7zTar.si = FALSE;
    if(P7zipInit(&p7zTar))
        return -1;
    
    memset(&p7zGzip, 0, sizeof(P7zip));
    p7zGzip.bin = P7Z_GUI;    
    p7zGzip.action = P7Z_ACTION_ADD;
    p7zGzip.typePara = P7Z_TYPE_GZIP;
    p7zGzip.files = NULL;
    p7zGzip.fileCount = 0;
    p7zGzip.archive = archive;
    p7zGzip.cl = cl;
    p7zGzip.mt = -1;
    p7zGzip.volSize = vol;
    p7zGzip.passwd = NULL;
    p7zGzip.so = FALSE;
    p7zGzip.si = TRUE;
    if(P7zipInit(&p7zGzip)){
        return -1;
    }

    P7zipPopen(&p7zTar, &outFd, &pid);
    stdinFd = dup(0);
    dup2(outFd, 0);
    close(outFd);
    ret = P7zipSystem(&p7zGzip);
    dup2(stdinFd, 0);
//    close(outFd);
    close(stdinFd);
    waitpid(pid, &status, 0);
    if(WIFEXITED(status)){
        ret = WEXITSTATUS(status)|ret;
    }else{
        ret = -1;
    }
    P7zipExit(&p7zTar);
    P7zipExit(&p7zGzip);
    return ret;
}

int CompressTarBzip2(int fileCount, const char* files[], const char* archive, int cl, int mt, long long vol)
{
    P7zip p7zTar, p7zGzip;
    int outFd, stdinFd;
    pid_t pid;
    int ret;
    int status;
    
    memset(&p7zTar, 0, sizeof(P7zip));
    p7zTar.bin = P7Z_CLI;
    p7zTar.action = P7Z_ACTION_ADD;
    p7zTar.typePara = P7Z_TYPE_TAR;
    p7zTar.files = files;
    p7zTar.fileCount = fileCount;
    p7zTar.archive = "lijuan_dummy.tar";
    p7zTar.cl = -1;
    p7zTar.mt = mt;
    p7zTar.volSize = -1;
    p7zTar.passwd = NULL;
    p7zTar.so = TRUE;
    p7zTar.si = FALSE;
    P7zipInit(&p7zTar);
    
    memset(&p7zGzip, 0, sizeof(P7zip));
    p7zGzip.bin = P7Z_GUI;
    p7zGzip.action = P7Z_ACTION_ADD;
    p7zGzip.typePara = P7Z_TYPE_BZIP2;
    p7zGzip.files = NULL;
    p7zGzip.fileCount = 0;
    p7zGzip.archive = archive;
    p7zGzip.cl = cl;
    p7zGzip.mt = mt;
    p7zGzip.volSize = vol;
    p7zGzip.passwd = NULL;
    p7zGzip.so = FALSE;
    p7zGzip.si = TRUE;
    P7zipInit(&p7zGzip);

    ret = P7zipPopen(&p7zTar, &outFd, &pid);
    stdinFd = dup(0);
    dup2(outFd, 0);
    P7zipSystem(&p7zGzip);
    dup2(stdinFd, 0);
    close(outFd);
    close(stdinFd);
    waitpid(pid, &status, 0);
    if(WIFEXITED(status)){
        ret = WEXITSTATUS(status)|ret;
    }else{
        ret = -1;
    }
    P7zipExit(&p7zTar);
    P7zipExit(&p7zGzip);
    return ret;
}

int Extract(const char* file, const char* dest)
{
    int ret;
    P7zip p7z;
    memset(&p7z, 0, sizeof(P7zip));
    
    p7z.bin = P7Z_GUI;
    p7z.action = P7Z_ACTION_EXTRACT_WITH_PATH;
    p7z.typePara = NULL;
    p7z.files = NULL;
    p7z.fileCount = 0;
    p7z.archive = file;
    p7z.cl = -1;
    p7z.mt = -1;
    p7z.volSize = -1;
    p7z.passwd = NULL;
    p7z.so = FALSE;
    p7z.si = FALSE;
    p7z.outDir = dest;
    if(P7zipInit(&p7z))
        return -1;
    ret = P7zipSystem(&p7z);
    P7zipExit(&p7z);
    return ret;
}

int ExtractTarXX(const char* file, const char* dest)
{
    P7zip p7zGzip;
    int outFd, stdinFd;
    pid_t pid;
    int ret;
    int status;
    
    memset(&p7zGzip, 0, sizeof(P7zip));
    p7zGzip.bin = P7Z_GUI;
    p7zGzip.action = P7Z_ACTION_EXTRACT_WITH_PATH;
    p7zGzip.typePara = NULL;
    p7zGzip.files = NULL;
    p7zGzip.fileCount = 0;
    p7zGzip.archive = file;
    p7zGzip.cl = -1;
    p7zGzip.mt = -1;
    p7zGzip.volSize = -1;
    p7zGzip.passwd = NULL;
    p7zGzip.so = TRUE;
    p7zGzip.si = FALSE;
    P7zipInit(&p7zGzip);

    ret = P7zipPopen(&p7zGzip, &outFd, &pid);
    stdinFd = dup(0);
    dup2(outFd, 0);
    ret = CFSystem2(NULL, NULL, "tar", "x", "-C", dest, NULL);
    dup2(stdinFd, 0);
    close(outFd);
    close(stdinFd);
    waitpid(pid, &status, 0);
    if(WIFEXITED(status)){
        ret |= WEXITSTATUS(status);
    }else{
        ret = -1;
    }
    P7zipExit(&p7zGzip);
    return ret;
}
